package com.terracloud.server.service.entitiy.device.group;

import com.terracloud.server.common.data.DeviceGroup;
import com.terracloud.server.common.data.audit.ActionType;
import com.terracloud.server.common.data.id.DeviceGroupId;
import com.terracloud.server.common.data.id.TenantId;
import com.terracloud.server.common.data.page.PageData;
import com.terracloud.server.common.data.page.PageLink;
import com.terracloud.server.dao.model.sql.DeviceGroupEntity;
import com.terracloud.server.dao.model.sql.DeviceGroupRelationEntity;
import com.terracloud.server.dao.sql.device.DeviceGroupRelationRepository;
import com.terracloud.server.dao.sql.device.DeviceGroupRepository;
import com.terracloud.server.dao.sql.device.DeviceRepository;
import com.terracloud.server.queue.util.TbCoreComponent;
import com.terracloud.server.service.entitiy.AbstractTbEntityService;
import com.terracloud.server.service.security.model.SecurityUser;
import com.terracloud.server.utils.PageDataUtils;
import com.terracloud.server.vo.DeviceGroupRelationUpdateVo;
import jakarta.persistence.criteria.Predicate;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @author chenhainan
 * @since 2025/10/11
 */
@AllArgsConstructor
@TbCoreComponent
@Service
@Slf4j
public class DefaultTbDeviceGroupService extends AbstractTbEntityService implements TbDeviceGroupService {

    private final DeviceGroupRepository deviceGroupRepository;

    private final DeviceGroupRelationRepository deviceGroupRelationRepository;

    private final DeviceRepository deviceRepository;

    private final JdbcTemplate jdbcTemplate;


    @Override
    public DeviceGroup saveDeviceGroup(DeviceGroup deviceGroup, SecurityUser currentUser) {
        ActionType actionType = deviceGroup.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
        DeviceGroupEntity deviceGroupEntity = new DeviceGroupEntity(deviceGroup);
        if (actionType == ActionType.ADDED) {
            deviceGroupEntity.setCreatedTime(System.currentTimeMillis());
        }
        deviceGroupRepository.save(deviceGroupEntity);
        deviceGroup.setId(new DeviceGroupId(deviceGroupEntity.getId()));
        autoCommit(currentUser, deviceGroup.getId());
        logEntityActionService.logEntityAction(deviceGroup.getTenantId(), deviceGroup.getId(), deviceGroup, actionType, currentUser);
        return deviceGroup;
    }

    @Override
    public DeviceGroup findDeviceGroupById(TenantId tenantId, DeviceGroupId deviceGroupId) {
        return deviceGroupRepository.findDeviceGroupByTenantIdAndId(tenantId.getId(), deviceGroupId.getId()).toData();
    }

    @Override
    public DeviceGroup findDeviceGroupInfoById(TenantId tenantId, DeviceGroupId id) {
        final var deviceGroup = findDeviceGroupById(tenantId, id);
        if (deviceGroup == null) {
            return null;
        }
        final var deviceIds = deviceGroupRelationRepository.findAllByDeviceGroupId(deviceGroup.getUuidId()).stream()
                .map(DeviceGroupRelationEntity::getDeviceId)
                .toList();
        if (deviceIds.isEmpty()) {
            deviceGroup.setDevices(Collections.emptyList());
        } else {
            final var deviceGroupDeviceInfoList = deviceRepository.findAllDeviceIdNameInfoByDeviceIds(deviceIds);
            deviceGroup.setDevices(deviceGroupDeviceInfoList);
        }
        return deviceGroup;
    }

    @Override
    @Transactional
    public void delete(DeviceGroup deviceGroup, SecurityUser currentUser) {
        deviceGroupRepository.deleteById(deviceGroup.getUuidId());
        deviceGroupRelationRepository.deleteByDeviceGroupId(deviceGroup.getUuidId());
        logEntityActionService.logEntityAction(deviceGroup.getTenantId(), deviceGroup.getId(), deviceGroup, ActionType.DELETED, currentUser);

    }

    @Override
    public PageData<DeviceGroup> findDeviceGroups(TenantId tenantId, PageLink pageLink) {
        Specification<DeviceGroupEntity> condition = (root, query, builder) -> {
            List<Predicate> conditions = new ArrayList<>();
            conditions.add(builder.equal(root.get("tenantId"), tenantId.getId()));
            if (StringUtils.hasText(pageLink.getTextSearch())) {
                conditions.add(builder.like(root.get("name"), '%' + pageLink.getTextSearch() + '%'));
            }
            return builder.and(conditions.toArray(new Predicate[0]));
        };
        try {
            return PageDataUtils.toPageDataAsync(deviceGroupRepository.findAll(condition, PageRequest.of(pageLink.getPage(), pageLink.getPageSize())),
                    deviceGroupEntity -> {
                        final var deviceGroup = deviceGroupEntity.toData();
                        deviceGroup.setDeviceCount(deviceGroupRelationRepository.countByDeviceGroupId(deviceGroupEntity.getId()));
                        return deviceGroup;
                    });
        } catch (Exception e) {
            log.error("查询设备分组发生异常", e);
            return new PageData<>();
        }
    }

    @Override
    public void addDeviceToGroup(SecurityUser currentUser, DeviceGroupRelationUpdateVo deviceGroupRelationUpdateVo) {
        final var existDeviceIds = deviceGroupRelationRepository.findAllByDeviceGroupId(deviceGroupRelationUpdateVo.getDeviceGroupId())
                .stream().map(DeviceGroupRelationEntity::getDeviceId).collect(Collectors.toSet());
        final var groupRelationEntityList = deviceGroupRelationUpdateVo.getDeviceIds().stream().filter(deviceId -> !existDeviceIds.contains(deviceId))
                .map(deviceId -> {
                    final var deviceGroupRelationEntity = new DeviceGroupRelationEntity(deviceId, deviceGroupRelationUpdateVo.getDeviceGroupId());
                    deviceGroupRelationEntity.setCreatedTime(System.currentTimeMillis());
                    return deviceGroupRelationEntity;
                }).toList();
        deviceGroupRelationRepository.saveAll(groupRelationEntityList);
    }


    @Override
    @Transactional
    public void removeDeviceFromGroup(SecurityUser currentUser, DeviceGroupRelationUpdateVo deviceGroupRelationUpdateVo) {
        deviceGroupRelationRepository.removeAllByDeviceGroupIdIsAndDeviceIdIn(deviceGroupRelationUpdateVo.getDeviceGroupId(), deviceGroupRelationUpdateVo.getDeviceIds());
    }
}
